module frame_sync (
           input wire clk,
           input wire rst_n,
           input wire din,
           input wire din_valid,
           output wire sync,
           output wire [7: 0] control,
           output wire [7: 0] length,
           output wire [7: 0] dout,
           output wire dout_valid,
           output wire crc_check,
           output wire recv_done
       );

`define WAIT_SYNC 3'b000
`define RECV_CONTROL 3'b001
`define RECV_LENGTH 3'b010
`define RECV_DATA 3'b100
`define RECV_CRC_1 3'b101
`define RECV_CRC_2 3'b110

`define SYNC_WORD 32'h0dd4259f

reg [2: 0] status;

// 32位 移位寄存器，用于同步头的接收
wire [31: 0] sync_shift_register;
reg sync_din_valid;
shift_register #(
    .DW(32)
) u_sync_shift_register ( 
    .clk       (clk),
    .rst_n     (rst_n),
    .din       (din),
    .din_valid (sync_din_valid & din_valid),
    .dout      (sync_shift_register)
);

// 8位 移位寄存器，用于数据串行转并行接收
wire [7: 0] data_shift_register;
wire [8: 0] data_shift_cnt;
reg data_din_valid;
shift_register #(
    .DW(8)
) u_data_shift_register (
    .clk       (clk),
    .rst_n     (rst_n),
    .din       (din),
    .din_valid (data_din_valid & din_valid),
    .dout      (data_shift_register),
    .cnt       (data_shift_cnt)
);

// CRC校验模块
wire [15: 0] crc_result;
reg crc_din_valid;
reg crc_clear;
crc16 u_crc16 (
    .clk       (clk),
    .rst_n     (rst_n),
    .clear     (crc_clear),
    .din       (din),
    .din_valid (crc_din_valid),
    .crc       (crc_result)
);

reg [7: 0] ctrl_word; // 控制字寄存器
reg [7: 0] length_word; // 长度寄存器
reg [7: 0] data_word; // 数据寄存器
reg [7: 0] crc_word; // CRC校验码高8位寄存器
reg [8: 0] data_cnt; // 数据接收计数器
reg dout_valid_reg; // 输出有效寄存器
reg crc_check_reg; // CRC校验有效寄存器
reg sync_reg; // 同步成功标志寄存器
reg recv_done_reg; // 接收完成标志寄存器

assign sync = sync_reg;
assign control = ctrl_word;
assign length = length_word;
assign dout = data_word;
assign dout_valid = dout_valid_reg;
assign crc_check = crc_check_reg;
assign recv_done = recv_done_reg;

always @(posedge clk) begin
    if (!rst_n) begin
        status <= `WAIT_SYNC; // 默认为WAIT_SYNC状态
        
        // 输出寄存器全部设置为0
        ctrl_word <= 0;
        length_word <= 0;
        data_word <= 0;
        dout_valid_reg <= 0;
        crc_check_reg <= 0;
        sync_reg <= 0;
        recv_done_reg <= 0;

        crc_word <= 0;
        
        // 内部模块控制寄存器
        crc_din_valid <= 0; // CRC模块默认关机
        crc_clear <= 0; // CRC默认不清除状态
        sync_din_valid <= 1; // 默认同步移位寄存器开机
        data_din_valid <= 0; // 默认数据移位寄存器关机
        data_cnt <= 0; // 数据接收计数器默认为0
        
        
    end else begin
        case (status)
            
            `WAIT_SYNC: begin
                sync_reg <= 0; // 同步完成
                crc_clear <= 0; // 关闭CRC清除状态
                crc_check_reg <= 0; // 清除上次crc检查状态
                recv_done_reg <= 0; // 清除上次数据输出状态

                if (sync_shift_register == `SYNC_WORD) begin
                    status <= `RECV_CONTROL; // 切换状态
                    crc_din_valid <= 1; // CRC校验开机
                    sync_din_valid <= 0; // 同步移位寄存器关机
                    data_din_valid <= 1; // 数据移位寄存器开机
                    sync_reg <= 1; // 同步开始
                end else ;
            end

            `RECV_CONTROL: begin
                if (data_shift_cnt == 8) begin
                    status <= `RECV_LENGTH; // 切换状态
                    ctrl_word <= data_shift_register; // 移位寄存器数据保存
                end
            end

            `RECV_LENGTH: begin
                if (data_shift_cnt == 8) begin
                    data_cnt <= 0; // 清空数据读取量，准备开始读取数据
                    length_word <= data_shift_register; // 移位寄存器数据保存
                    if (data_shift_register)
                        status <= `RECV_DATA; // 切换状态
                    else begin
                        crc_din_valid <= 0;
                        status <= `RECV_CRC_1;
                    end
                end
            end

            `RECV_DATA: begin
                if (data_shift_cnt == 8) begin
                    dout_valid_reg <= 1; // 在本周期中有数据输出

                    if (data_cnt + 1 == length_word) begin // 数据长度已经足够
                        status <= `RECV_CRC_1; // 切换状态
                        data_cnt <= 0; // 计数器归零
                        crc_din_valid <= 0; // CRC校验关机
                    end else begin
                        data_cnt <= data_cnt + 1; // 计数器计数
                    end

                    data_word <= data_shift_register; // 移位寄存器数据保存

                end else begin
                    dout_valid_reg <= 0; // 在本周期中数据不输出
                end
            end

            `RECV_CRC_1: begin
                dout_valid_reg <= 0; // 要关闭上次数据的输出指示
                if (data_shift_cnt == 8) begin
                    status <= `RECV_CRC_2;
                    crc_word <= data_shift_register;
                end
            end

            `RECV_CRC_2: begin
                if (data_shift_cnt == 8) begin
                    status <= `WAIT_SYNC; // 切换回最初的状态
                    if ({crc_word, data_shift_register} == crc_result) 
                        crc_check_reg <= 1;
                    else 
                        crc_check_reg <= 0;

                    recv_done_reg <= 1; // 标志接收完成
                    crc_clear <= 1; // 清除CRC状态
                    data_din_valid <= 0; // 数据移位寄存器关机
                    sync_din_valid <= 1; // 同步移位寄存器开机
                end
            end

        endcase
    end
end


endmodule

module shift_register
    #(
        parameter DW = 8
    ) (
        input wire clk,
        input wire rst_n,
        input wire din,
        input wire din_valid,
        output wire [DW - 1: 0] dout,
        output wire [8: 0] cnt
    );

reg [8: 0] input_cnt;
reg [DW - 1: 0] dout_reg;

assign dout = {dout_reg[DW - 2: 0], din};
assign cnt = input_cnt + 1;

always @(posedge clk) begin
    if (!rst_n) begin
        dout_reg <= 0;
        input_cnt <= 0;
    end else begin
        if (din_valid) begin
            dout_reg <= {dout_reg[DW - 2: 0], din};

            if (input_cnt + 1 == DW) 
                input_cnt <= 0;
            else
                input_cnt <= input_cnt + 1;
        end
    end
end

endmodule

module crc16
       #(
           parameter Initial = 16'h0000
       )
       (
           input wire clk,
           input wire rst_n,
           input wire clear,
           input wire din,
           input wire din_valid,
           output wire [15: 0] crc
       );

reg [15: 0] crc_reg;
assign crc = crc_reg;

wire xor_input = din ^ crc_reg[15];

always @(posedge clk) begin
    if (!rst_n) begin
        crc_reg <= Initial;
    end else begin
        if (clear) begin
            crc_reg <= Initial;
        end else begin
            if (din_valid) begin
                crc_reg <= {
                    crc_reg[14: 12], 
                    crc_reg[11] ^ xor_input, 
                    crc_reg[10: 5], 
                    crc_reg[4] ^ xor_input, 
                    crc_reg[3: 0], 
                    xor_input};
            end else ;
        end
    end
end

endmodule
